Let's dive into some specifics and code examples to
show how to secure your data in SQL Azure. You may need to secure
specific columns in your database that contain sensitive information,
such as Social Security numbers or credit card numbers. Certain
applications store patient data, which can fall under compliance review,
and as such may need to be encrypted as well. As hinted previously, not
all security mechanisms are currently available, so this section
focuses on what SQL Azure provides and on ways to mitigate the missing
features. Regarding data encryption, because SQL Azure provides none,
you see how to implement your own security classes to simplify data
encryption in your projects.
NOTE
The examples that follow use a database script called Security.sql and a Visual Studio 2008 project called SQLAzureSecurity.sln. You can run the SQL script on your local SQL Server database if you don't have a Windows Azure account yet.
This article uses a few classes and methods to demonstrate how to use encryption, hashing, and other techniques. Figure 1 shows the objects being used. The Encryption class performs the actual encryption and returns a CipherText structure; the UserProperties class uses extension methods from the Extensions class and a helper method in the Util class. The CDatabase class returns the database connection string.
1. Encryption
As mentioned
previously, data encryption isn't available. Why? Because SQL Azure
doesn't support X.509 certificates yet. Certificates are necessary for
many encryption-related features, such as Transparent Data Encryption
(TDE), column-level encryption, and certain T-SQL commands, such as FOR ENCRYPTION and SIGNBYCERT.
However, SQL Azure
requires the use of SSL encryption for its communication. This means
your sensitive data is always transmitted safely between your clients
and your SQL Azure database. There is nothing you need to do to enable
SSL encryption; it's required and automatically enforced by SQL Azure.
If an application tries to connect to SQL Azure and the application
doesn't support SSL, the connection request fails.
But SSL doesn't encrypt data at
rest; it only encrypts data in transit. How can you protect your data
when it's stored in SQL Azure? Because SQL Azure doesn't support
encryption natively, you must encrypt and decrypt your data in the
application code.
The Security.sql script contains the following T-SQL statement:
1. CREATE TABLE UserProperties
2. (
3. ID int identity(1,1) PRIMARY KEY, -- identity of the record
4. PropertyName nvarchar(255) NOT NULL, -- name of the property
5. Value varbinary(max) NOT NULL, -- encrypted value
6. Vector binary(16) NOT NULL, -- vector of encrypted value
7. LastUpdated datetime NOT NULL, -- date of last modification
8. Token binary(32) NOT NULL -- record hash
9. )
Each record contains a
property name (line 4) that can be used as a search key and an encrypted
value (line 5). The value itself is a binary data type, which lends
itself well to encryption. A vector is used for additional security;
this column is explained shortly. The Token and LastUpdated columns are
addressed later when discussing hashing.
The following C# code shows
how to encrypt a string value using the Advanced Encryption Standard
(AES) algorithm; you can easily add support for Triple Data Encryption
Standard (3DES) or other algorithms. It uses a shared secret to create
the ciphertext and returns a byte array. The byte array is stored later
in the Value column in the database:
1. /// <summary>
2. /// A result structure that stores the encrypted value
3. /// and its associated vector
4. /// </summary>
5. public struct CipherText
6. {
7. public byte[] cipher;
8. public byte[] vector;
9. }
10.
11. /// <summary>
12. /// The encryption class that encapsulates the complexity behind encrypting
13. /// and decrypting values
14. /// </summary>
15. public class Encryption
16. {
17. private byte[] _SECRET_KEY_ = new byte[] { 160, 225, 229, 3,
18. 148, 219, 67, 89, 247, 133, 213, 26, 129, 160, 235, 41,
19. 42, 177, 202, 251, 38, 56, 232, 90, 54, 88, 158, 169,
20. 200, 24, 19, 27 };
21.
22./// <summary>
23./// Encrypt using AES
24./// </summary>
25./// <param name="value">The string to encrypt</param>
26.public CipherText EncryptAES(string value)
27.{
28. // Prepare variables...
29. byte[] buffer = UTF8Encoding.UTF8.GetBytes(value);
30. CipherText ct = new CipherText();
31. System.Security.Cryptography.Aes aes = null;
32. ICryptoTransform transform = null;
33.
34. // Create the AES object
35. aes = System.Security.Cryptography.Aes.Create();
36. aes.GenerateIV();
37. aes.Key = _SECRET_KEY_;
38.
39. // Create the encryption object
40. transform = aes.CreateEncryptor();
41.
42. // Encrypt and store the result in the structure
43. ct.cipher = transform.TransformFinalBlock(buffer, 0, buffer.Length);
44. // Save the vector used for future use
45. ct.vector = aes.IV;
46.
47. return ct;
48. }
49.}
The CipherText structure
(line 5) is used as a return value. Each encrypted byte array comes with
its initialization vector, which is a security mechanism that prevents
dictionary attacks on your database. The Encryption class contains an EncryptAES method that performs the actual encryption of a string value; this method returns CipherText.
Because AES requires a
secret key, you created one in the form of a byte array on line 17. The
secret key must be 32 bytes in length. You can easily generate your own
by using the GenerateKey method provided by the Aes class provided by .NET.
On line 29, you transform
the string value to its byte representation using UTF-8 encoding. UTF-8
encoding is very practical because it automatically chooses between
ASCII and Unicode based on the input value.
You declare the AES object on line 31 and instantiate it on line 35 using the static Create() method on the Aes class. This method creates the vector automatically on line 36 and sets the private key discussed earlier.
On line 40, you create a cryptographic object using the CreateEncryptor() method. A call to its TransformFinalBlock() method does the trick and outputs a variable-length byte array that you store in the CipherText structure instance on line 43. You save the previously generated vector as well and return the structure on line 47.
That was simple, right? Now all you have to do is store the CipherText content in the UserProperties table. But before doing this, let's discuss hashing.
NOTE
This example uses AES,
but other algorithms are available with the .NET framework. Because you
also use an initialization vector, running the same code over and over
yields different output, given the same input. That makes the encrypted
value harder to crack. The Visual Studio Solution provided includes
additional methods to decrypt data.